package com.utils.oss;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.ListPartsRequest;
import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.model.PartListing;
import com.aliyun.oss.model.PartSummary;
import com.aliyun.oss.model.UploadPartRequest;
import com.aliyun.oss.model.UploadPartResult;
import com.aliyuncs.exceptions.ClientException;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;

/**
 * This sample demonstrates how to upload multiparts to Aliyun OSS using the OSS
 * SDK for Java.
 */
@Slf4j
public class MultipartUpload {
	
	private static String key = null;
	private static String endpoint = null;
	private static String accessKeyId = null;
	private static String accessKeySecret = null;
	private static String bucketName = null;
	private static String imeiPre = null;
	
	private static OSS client = null;
	private static List<PartETag> partETags = null;

	// 定长线程池
	private static ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

	private static final int partSize = 1024 * 1024; // 1MB 分片大小

	public static void main(String[] args) {
		System.out.println(2 * Runtime.getRuntime().availableProcessors());
	}
	
	/**
	 * 上传执行器
	 */
	public synchronized static String fileUploadImei(File file, String fileName,
			String endpoint, String accessKeyId, String accessKeySecret, String bucketName, String imeiPre
			) throws IOException {
		ReentrantLock lock = new ReentrantLock();
		lock.lock();
		MultipartUpload.endpoint = endpoint;
		MultipartUpload.accessKeyId = accessKeyId;
		MultipartUpload.accessKeySecret = accessKeySecret;
		MultipartUpload.bucketName = bucketName;
		MultipartUpload.imeiPre = imeiPre;
		long start = System.currentTimeMillis();
		try {
			String fileUrl = uploadImei(file, fileName);
			long end = System.currentTimeMillis();
			log.info("upload user imei multiparts,文件上传结束,共耗时" + (end - start) + "ms");
			return fileUrl;
		} finally {
			lock.unlock();
		}
	}

	private static String uploadImei(File file, String filename) {
//		long start = System.currentTimeMillis();
		key = imeiPre + filename;
		ClientConfiguration conf = new ClientConfiguration();
		conf.setIdleConnectionTime(600000);
		client = new OSSClient(endpoint, accessKeyId, accessKeySecret, conf);
		File sampleFile = null;
		try {
			String uploadId = claimUploadId(file.getName().replace(" ", ""));
			log.info("upload user imei multiparts,申请一个新的上传id:" + uploadId);
			// 需上传文件
			sampleFile = file;
			// 文件大小
			long fileLength = sampleFile.length();
			// 分片总数（总共分几个部分）
			int partCount = (int) (fileLength / partSize);
			if (fileLength % partSize != 0) {
				partCount++;
			}
			if (partCount > 10000) {
				log.warn("partCount总数不应超过10000");
				return null;
			} else {
				log.info("upload user imei multiparts,文件总共分片数:" + partCount);
			}
			partETags = Collections.synchronizedList(new ArrayList<PartETag>(partCount));
			CountDownLatch latch = new CountDownLatch(partCount);
			log.info("upload user imei multiparts,***************开始准备上传************");
			// threadPool = Constans.getMyThreadPool();
			for (int i = 0; i < partCount; i++) {
				long startPos = i * partSize;
				long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
				threadPool.execute(new PartUploader(sampleFile, startPos, curPartSize, i + 1, uploadId, latch));
			}
			latch.await();
			if (partETags.size() != partCount) {
				StringBuilder partETagsStr = new StringBuilder("(");
				for (PartETag item : partETags) {
					partETagsStr.append(item.getPartNumber()).append(",");
				}
				partETagsStr.append(")");
				log.info(String.format("upload user imei multiparts,partCount:%s*******,partEtages:%s*******,partETagsSize:%s", partCount,
						partETagsStr, partETags.size()));
				throw new IllegalStateException("upload user imei multiparts,上传多个部分失败，因为有些部分还没有完成");
			} else {
				log.info("upload user imei multiparts,成功地将多个部分合并上传到一个名为:{}的对象中 ", endpoint + key);
			}
			listAllParts(uploadId);
			completeMultipartUpload(uploadId);
//			log.info("获取一个对象");
//			long end = System.currentTimeMillis();
			// 生成文件地址
			// client.getObject(new GetObjectRequest(bucketName, key), new
			// File(localFilePath));
//			log.info(String.format("上传成功*****耗时：%s*****，文件地址：%s", ((end - start) / 1000), fileUrl));
		} catch (OSSException oe) {
			log.error(oe.getMessage(), oe);
//		} catch (ClientException ce) {
//			log.error(ce.getErrorMessage(), ce);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			if (client != null) {
				client.shutdown();
			}
			if (sampleFile != null) {
				sampleFile.delete();
			}
			partETags.clear();
			partETags = null;
		}
		return key;
	}

	public static final String getContentType(String fileName) {
		String FilenameExtension = fileName.substring(fileName.lastIndexOf("."));
		if (FilenameExtension.equalsIgnoreCase(".bmp")) {
			return "application/x-bmp";
		}
		if (FilenameExtension.equalsIgnoreCase(".gif")) {
			return "image/gif";
		}
		if (FilenameExtension.equalsIgnoreCase(".jpeg") || FilenameExtension.equalsIgnoreCase(".jpg")
				|| FilenameExtension.equalsIgnoreCase(".png")) {
			return "image/jpeg";
		}
		if (FilenameExtension.equalsIgnoreCase(".html")) {
			return "text/html";
		}
		if (FilenameExtension.equalsIgnoreCase(".txt")) {
			return "text/plain";
		}
		if (FilenameExtension.equalsIgnoreCase(".vsd")) {
			return "application/vnd.visio";
		}
		if (FilenameExtension.equalsIgnoreCase(".pptx") || FilenameExtension.equalsIgnoreCase(".ppt")) {
			return "application/vnd.ms-powerpoint";
		}
		if (FilenameExtension.equalsIgnoreCase(".docx") || FilenameExtension.equalsIgnoreCase(".doc")) {
			return "application/msword";
		}
		if (FilenameExtension.equalsIgnoreCase(".xla") || FilenameExtension.equalsIgnoreCase(".xlc")
				|| FilenameExtension.equalsIgnoreCase(".xlm") || FilenameExtension.equalsIgnoreCase(".xls")
				|| FilenameExtension.equalsIgnoreCase(".xlt") || FilenameExtension.equalsIgnoreCase(".xlw")) {
			return "application/vnd.ms-excel";
		}
		if (FilenameExtension.equalsIgnoreCase(".xml")) {
			return "text/xml";
		}
		if (FilenameExtension.equalsIgnoreCase(".pdf")) {
			return "application/pdf";
		}
		if (FilenameExtension.equalsIgnoreCase(".zip")) {
			return "application/zip";
		}
		if (FilenameExtension.equalsIgnoreCase(".tar")) {
			return "application/x-tar";
		}
		if (FilenameExtension.equalsIgnoreCase(".avi")) {
			return "video/avi";
		}
		if (FilenameExtension.equalsIgnoreCase(".mp4")) {
			return "video/mpeg4";
		}
		if (FilenameExtension.equalsIgnoreCase(".mp3")) {
			return "audio/mp3";
		}
		if (FilenameExtension.equalsIgnoreCase(".mp2")) {
			return "audio/mp2";
		}
		return "application/octet-stream";
	}

	private static String claimUploadId(String fileName) {
		InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);
		// 我本地上传mp4视频，大家可根据自己的文件类型，设置不同的响应content-type
		request.addHeader("Content-Type", getContentType(fileName));
		request.addHeader("Cache-Control", "no-cache");
		InitiateMultipartUploadResult result = client.initiateMultipartUpload(request);
		return result.getUploadId();
	}

	private static void listAllParts(String uploadId) {
		log.info("upload user imei multiparts,Listing all parts......");
		ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, key, uploadId);
		PartListing partListing = client.listParts(listPartsRequest);

		int partCount = partListing.getParts().size();
		for (int i = 0; i < partCount; i++) {
			PartSummary partSummary = partListing.getParts().get(i);
			log.info("upload user imei multiparts,Part#" + partSummary.getPartNumber() + ", ETag=" + partSummary.getETag());
		}
	}

	private static void completeMultipartUpload(String uploadId) {
		// Make part numbers in ascending order
		Collections.sort(partETags, new Comparator<PartETag>() {

			@Override
			public int compare(PartETag p1, PartETag p2) {
				return p1.getPartNumber() - p2.getPartNumber();
			}
		});

		log.info("upload user imei multiparts,Completing to upload multiparts\n");
		CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName,
				key, uploadId, partETags);
		client.completeMultipartUpload(completeMultipartUploadRequest);
	}
	
	/**
	 * 静态内部类，上传组件
	 */
	private static class PartUploader implements Runnable {
		private File localFile;
		private long partSize;
		private int partNumber;
		private String uploadId;
		private long startPos;
		private CountDownLatch latch;

		public PartUploader(File localFile, long startPos, long partSize, int partNumber, String uploadId,
				CountDownLatch latch) {
			this.localFile = localFile;
			this.partSize = partSize;
			this.partNumber = partNumber;
			this.uploadId = uploadId;
			this.startPos = startPos;
			this.latch = latch;
		}

		@Override
		public void run() {
			InputStream instream = null;
			try {
				log.info("upload user imei multiparts,Part#" + this.partNumber + " 开始上传\n");
				instream = new FileInputStream(localFile);
				instream.skip(startPos);
				UploadPartRequest uploadPartRequest = new UploadPartRequest();
				uploadPartRequest.setBucketName(bucketName);
				uploadPartRequest.setKey(key);
				uploadPartRequest.setUploadId(this.uploadId);
				uploadPartRequest.setInputStream(instream);
				uploadPartRequest.setPartSize(this.partSize);
				uploadPartRequest.setPartNumber(this.partNumber);
				UploadPartResult uploadPartResult = client.uploadPart(uploadPartRequest);
				log.info("upload user imei multiparts,Part#" + this.partNumber + " 完毕\n");
				synchronized (partETags) {
					partETags.add(uploadPartResult.getPartETag());
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (instream != null) {
					try {
						instream.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				latch.countDown();
			}
		}
	}

}
